package cn.ytxy.double_system_data_visualization_demo.user.service.Impl;

import cn.ytxy.double_system_data_visualization_demo.base.exception.ServiceException;
import cn.ytxy.double_system_data_visualization_demo.base.response.StatusCode;
import cn.ytxy.double_system_data_visualization_demo.user.mapper.ScoreChangeMapper;
import cn.ytxy.double_system_data_visualization_demo.user.mapper.UserMapper;
import cn.ytxy.double_system_data_visualization_demo.user.pojo.dto.ScoreChangeParam;
import cn.ytxy.double_system_data_visualization_demo.user.pojo.dto.UserLoginParam;
import cn.ytxy.double_system_data_visualization_demo.user.pojo.dto.UserQuery;
import cn.ytxy.double_system_data_visualization_demo.user.pojo.dto.UserSaveParam;
import cn.ytxy.double_system_data_visualization_demo.user.pojo.entity.ScoreChange;
import cn.ytxy.double_system_data_visualization_demo.user.pojo.entity.User;
import cn.ytxy.double_system_data_visualization_demo.user.pojo.vo.UserVO;
import cn.ytxy.double_system_data_visualization_demo.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.UUID;


@Slf4j
/*@Service注解表示当前类为业务层实现类*/
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;
    @Autowired
    ScoreChangeMapper scoreChangeMapper;

    /**
     * 业务层处理登陆的逻辑，登陆成功返回用户信息，失败抛出异常，让全局异常处理器处理
     *
     * @param userLoginParam
     * @return JsonResult/ 失败抛出ServiceException自定义异常
     * @author renhongDeng~
     */
    @Override
    public UserVO login(UserLoginParam userLoginParam) {
        log.debug("登陆业务：userLoginParam={}", userLoginParam);
        UserVO userVO = userMapper.selectByUserName(userLoginParam.getUsername());
        if (userVO == null) {
            //不能return需要抛出异常
            throw new ServiceException(StatusCode.USERNAME_ERROR);
        }
        if (!userVO.getPassword().equals(userLoginParam.getPassword())) {
            throw new ServiceException(StatusCode.PASSWORD_ERROR);
        }
        userVO.setToken(UUID.randomUUID().toString().replace("-", ""));

        userMapper.updateTokenByUserName(userVO.getToken(), userLoginParam.getUsername());
        return userVO;
    }

    /**
     * 选择用户信息
     * <p>
     * 此方法用于根据用户查询对象查询用户信息
     * 它覆盖了接口中的默认方法，以便提供具体的实现
     *
     * @param userQuery 用户查询对象，包含查询条件
     * @return 返回一个用户信息列表，每个元素都是一个用户信息的值对象
     */
    @Override
    public List<UserVO> selectUser(UserQuery userQuery) {
        // 记录查询日志，以便于调试和追踪
        log.debug("查询业务：userQuery={}", userQuery);

        // 调用用户数据访问层的查询方法，传入用户查询对象，并返回查询结果
        return userMapper.selectUser(userQuery);
    }


    /**
     * 保存用户信息
     * 如果用户ID为空，则插入新用户；否则，更新现有用户信息
     *
     * @param userSaveParam 用户保存参数，包含需要保存的用户信息
     */
    @Override
    public void saveUser(UserSaveParam userSaveParam) {
        // 记录保存用户的调试信息
        log.debug("保存业务：userSaveParam={}", userSaveParam);

        // 创建一个新的User对象，并将userSaveParam中的属性复制到User对象中
        User user = new User();
        BeanUtils.copyProperties(userSaveParam, user);

        // 检查用户ID是否为空，如果为空，则插入新用户，并初始化用户的总积分或余额为0
        if (user.getId() == null) {
            userMapper.insertUser(user);
            user.setTotal(0);
        }

        // 更新用户信息，无论用户ID是否为空，都会执行更新操作
        // 如果用户ID为空，这将是插入新用户后的首次更新
        userMapper.updateUser(user);
    }


    /**
     * 重写删除用户方法
     * <p>
     * 此方法接收一个用户ID，并调用用户映射器（UserMapper）的删除方法来删除指定的用户
     * 它主要用于响应删除用户的业务需求，并在日志中记录删除操作
     *
     * @param userId 要删除的用户的ID
     */
    @Override
    public void deleteUser(Long userId) {
        // 记录删除用户的业务日志
        log.debug("删除业务：userId={}", userId);
        // 调用映射器方法删除用户
        userMapper.deleteUser(userId);
    }


    @Override
    @Transactional
    public void addScore(Long userId, int score, String reason) {
        // 记录加分业务的日志信息
        log.debug("加分业务：userId={},score={},reason={}", userId, score, reason);

        // 检查用户ID是否为空，如果为空则抛出未登录异常
        if (userId == null) {
            throw new ServiceException(StatusCode.NOT_LOGIN);
        }

        // 根据用户ID查询用户信息
        User user = userMapper.selectById(userId);

        // 检查用户是否存在，抛出更明确的错误码
        if (user == null) {
            throw new ServiceException(StatusCode.OPERATION_FAILED);
        }

        // 对 score 设置合理范围限制，防止恶意输入
        final int MAX_SCORE_CHANGE = 1000;
        final int MIN_SCORE_CHANGE = -1000;
        if (score > MAX_SCORE_CHANGE || score < MIN_SCORE_CHANGE) {
            throw new ServiceException(StatusCode.OPERATION_FAILED);
        }

        // 对 reason 长度限制，防止插入失败
        final int MAX_REASON_LENGTH = 255;
        if (reason != null && reason.length() > MAX_REASON_LENGTH) {
            reason = reason.substring(0, MAX_REASON_LENGTH); // 截断处理
        }

        // 更新用户的总分数，处理 null 情况
        Integer currentTotal = user.getTotal();
        int newTotal = (currentTotal == null ? 0 : currentTotal) + score;
        user.setTotal(newTotal);

        // 更新用户信息到数据库
        userMapper.updateUser(user);

        // 创建分数变化记录对象
        ScoreChangeParam scoreChangeParam = new ScoreChangeParam();
        scoreChangeParam.setUserId(userId);
        scoreChangeParam.setReason(reason);
        scoreChangeParam.setPoints(score);
        scoreChangeParam.setDate(new Date());

        // 将分数变化记录添加到数据库
        scoreChangeMapper.addScore(scoreChangeParam);

        log.debug("加分业务完成：userId={},newTotal={}", userId, newTotal);
    }


    /**
     * 重写减分方法，用于扣除用户的分数
     * 此方法使用了事务注解，确保操作的原子性
     *
     * @param userId 用户ID，用于识别需要减分的用户
     * @param score  需要扣除的分数，正整数
     * @param reason 扣分的原因，用于记录
     * @throws ServiceException 当用户未登录或分数不足时抛出服务异常
     */
    @Override
    @Transactional
    public void subtractScore(Long userId, int score, String reason) {
        // 记录减分业务的日志信息
        log.debug("减分业务：userId={},score={},reason={}", userId, score, reason);

        // 根据用户ID查询用户信息
        User user = userMapper.selectById(userId);

        // 检查用户是否有足够的分数进行扣除
        if ((user.getTotal() - score) < 0) {
            throw new ServiceException(StatusCode.OPERATION_FAILED);
        }

        // 更新用户的总分数
        user.setTotal(user.getTotal() - score);

        // 更新用户信息
        userMapper.updateUser(user);

        // 创建分数变更记录对象
        ScoreChangeParam scoreChangeParam = new ScoreChangeParam();
        scoreChangeParam.setUserId(userId);
        scoreChangeParam.setReason(reason);
        scoreChangeParam.setPoints(-score);
        scoreChangeParam.setDate(new Date());

        // 记录分数扣除操作
        scoreChangeMapper.subtractScore(scoreChangeParam);
    }

}






